<?php
namespace StoreAdmin\Controller;

use Think\Controller;

import('Vendor.Wechat.Wechat');
import('Vendor.Wechat.TPWechat');
class WechatDispatcherController extends Controller
{

    public function index()
    {
        $wechat_id   = I('get.id/d');
        $wechat_info = $this->getWechatCache($wechat_id);

        $options = array(
            'token'          => $wechat_info['wechat_token'], //填写你设定的key
            'encodingaeskey' => $wechat_info['wechat_encodingaeskey'], //填写加密用的EncodingAESKey
            'appid'          => $wechat_info['wechat_appid'], //填写高级调用功能的app id
            'appsecret'      => $wechat_info['wechat_appsecret'], //填写高级调用功能的密钥
        );

        $wechat = \TPWechat::getInstance($options);
        $wechat->valid(false);
        $type = $wechat->getRev()->getRevType();
        $wechat->setProperty(array(
            'wechat_info' => $wechat_info,
        ));

        $this->doRec($wechat);

        exit;
    }

    private function doRec(&$wechat)
    {
        $wechat_info = $wechat->getProperty('wechat_info');
        if($wechat_info['is_third_auth']){
            return false;
        }
        if (!$wechat_info['wechat_state']) {
            return false;
        }

        $type    = $wechat->getRev()->getRevType();
        $message = $wechat->getRev()->getRevData();
        $openid  = $message['FromUserName'];
        $wechat->setProperty(array(
            'message' => $message,
            'openid'  => $openid,
        ));
        D('WechatMessage')->add(array(
            'info_type' => $type,
            'message'   => json_encode($message, JSON_UNESCAPED_UNICODE),
            'created'   => date('Y-m-d H:i:s'),
            'openid'    => $openid,
            'wechat_id' => $wechat_info['id'],
        ));

        switch ($type) {
            case $wechat::MSGTYPE_TEXT:

                $content          = trim($wechat->getRev()->getRevContent());
                $extw             = '';
                $content && $extw = "FIND_IN_SET('" . $content . "',tag) ";
                $replys           = $this->buildMessage($wechat, $extw);
                $replys && $this->sendMultipleMessage($wechat, $replys);
                !$replys && $this->doNoMessage($wechat);
                break;
            case $wechat::MSGTYPE_EVENT:
                $this->doEvent($wechat);
                break;
            case $wechat::MSGTYPE_LOCATION:
                $this->doLocation($wechat);
                break;
            default:

        }
    }

    /**
     * 构造素材消息
     * @param  [type]
     * @param  integer
     * @return [distribute] 1表示普通消息，2表示关注时回复，3表示无索引回复
     */
    private function buildMessage(&$wechat, $extw = null)
    {
        $wechat_info = $wechat->getProperty('wechat_info');
        $company_uid = $wechat_info['company_uid'];
        $w           = "mpid=0 and state=1 and company_uid = " . $company_uid;
        $extw && $w .= ' and ' . $extw;
        $rs = (array) D('material')->where($w)->select();

        $replys = [];
        foreach ($rs as $v) {
            $replys[$v['type']][] = $v;
        }

        return $replys;
    }

    /**
     * 素材回复
     * @param  [type]
     * @param  [type]
     * @param  boolean
     * @return [type]
     */
    private function doMessage(&$wechat, $replys, $return = false)
    {
        $r;

        foreach ((array) $replys as $key => $v) {
            switch ($key) {
                case 'text':
                    $v[0]['info'] = json_decode($v[0]['info'], true);
                    $msg          = $this->getInjectMessage($wechat, $v[0]['info']);
                    $r[]          = $wechat->text($msg)->reply(null, $return);
                    break;
                case 'single':
                    $newsData;
                    foreach ($v as $v1) {
                        $item       = json_decode($v1['info'], true);
                        $newsData[] = array(
                            'Title'       => $item['title'],
                            'Description' => $item['introduction'],
                            'PicUrl'      => $this->fullServerPath($item['cover']),
                            'Url'         => $item['url'],
                        );
                    }
                    $r[] = $wechat->news($newsData)->reply(null, $return);
                    break;
                case 'image':
                    $media_id         = $this->getMediaId($v[0], $key, $wechat);
                    $media_id && $r[] = $wechat->image($media_id)->reply(null, $return);
                    break;
                case 'voice':

                    $media_id         = $this->getMediaId($v[0], $key, $wechat);
                    $media_id && $r[] = $wechat->voice($media_id)->reply(null, $return);
                    break;
                case 'video':
                    $media_id = $this->getMediaId($v[0], $key, $wechat);
                    $item     = json_decode($v[0]['info'], true);

                    $media_id && $r[] = $wechat->video($media_id, $item['title'], $item['desc'])->reply(null, $return);

                    break;
            }

        }
        return $r;

    }

    /**
     * 消息注入
     * @param  [type] $msg [description]
     * @return [type]      [description]
     */
    private function getInjectMessage(&$wechat, $msg)
    {
        if (strpos($msg, '{@nickname}') !== false) {
            $openid = $wechat->getProperty('openid');
            $r      = D('wechat_fans')->where(array('openid' => $openid))->getField('nick_name');
            $msg    = str_replace('{@nickname}', $r, $msg);
        }
        if (strpos($msg, '{@date}') !== false) {
            $openid = $wechat->getProperty('openid');
            $r      = D('wechat_fans')->where(array('openid' => $openid))->getField('follow_time');
            $msg    = str_replace('{@date}', substr($r, 0, 10), $msg);
        }

        if (strpos($msg, '{@openid}') !== false) {
            $openid = $wechat->getProperty('openid');
            $msg    = str_replace('{@openid}', $openid, $msg);
        }

        if (strpos($msg, '{@city}') !== false) {
            $openid = $wechat->getProperty('openid');
            $r      = D('wechat_fans')->where(array('openid' => $openid))->getField('region');
            $r      = $r == '-' ? '' : $r;
            $msg    = str_replace('{@city}', $r, $msg);
        }

        $msg = preg_replace_callback("/<a(.*?)<\/a>/is", function ($v) {
            $v[1] = str_replace(array('target="_blank"', '"'), array('', "'"), $v[1]);
            return '<a ' . $v[1] . '</a>';
        }, $msg);

        return $msg;
    }

    /**
     * 发送无索引回复
     * @param  [type]
     * @return [type]
     */
    private function doNoMessage(&$wechat)
    {
        $wechat_info = $wechat->getProperty('wechat_info');
        $wechat_id   = $wechat_info['id'];
        $replys      = $this->subscribeSource($wechat, 3);
        $replys && $this->sendMultipleMessage($wechat, $replys);
        $this->doCustomerService($wechat);

    }

    /**
     * 转发人工客服
     * @param  [type]
     * @return [type]
     */
    private function doCustomerService(&$wechat)
    {
        $wechat_info = $wechat->getProperty('wechat_info');
        $rules       = D('wechat_rule')->where(array('mpid' => $wechat_info['id']))->getField('rules');
        $rules       = json_decode($rules, true);

        switch ($rules['regong']) {
            case 2:
                //客服消息转发
                $wechat->transfer_customer_service()->reply();
                break;

            case 1:
                //客服消息转发
                $now_w = date('w');
                $now_t = date('H:i');
                if (($now_w >= $rules['t'][0] && $now_w <= $rules['t'][1]) && ($now_t >= $rules['t'][2] && $now_t <= $rules['t'][3])) {
                    $wechat->transfer_customer_service()->reply();
                }
                break;

        }
    }

    /**
     * 关注时回复消息提取
     * @param  [type]
     * @param  [mtype] 2关注是回复，3表示无索引回复,4点击回复
     * @return [type]
     */
    private function subscribeSource(&$wechat, $mtype)
    {
        $wechat_info = $wechat->getProperty('wechat_info');
        $wechat_id   = $wechat_info['id'];
        $main        = D('wechat_rule')->where(array('mpid' => $wechat_id))->find();
        $cfg         = array(2 => 'subscribe', 3 => 'noindex', 4 => 'clickmenu');
        $r           = $main[$cfg[$mtype]];
        $replys      = [];
        if ($r) {
            $r           = json_decode($r, true);
            $main['cfg'] = $r;
            $replys      = call_user_func(array(&$this, "subscribeSource" . $mtype), $wechat, $main);

        }

        return $replys;
    }

    /**
     * 发送关注时回复
     * @param  [type] &$wechat [description]
     * @param  [type] $main    [description]
     * @return [type]          [description]
     */
    private function subscribeSource2(&$wechat, $main)
    {
        $r      = $main['cfg'];
        $replys = [];
        if ($r['enable'] && $r['material']) {
            $r['material'] = json_decode($r['material'], true);
            $ids           = getByField($r['material'], 'id', 'str');
            $materials     = D('Material')->where("id in (" . $ids . ") and state=1 ")->getField('id,type,title,info');
            foreach ($r['material'] as $v) {
                $item = $materials[$v['id']];
                if (array_sum($v['donation_target']) > 0) {
                    $item['donation_target'] = $v['donation_target'];
                    $item['outoftime']       = $r['cycle_start'] . ',' . $r['cycle_end'];
                    $this->dispatcherService($wechat, $item);
                } else {
                    $replys[$item['type']][] = $item;
                }
            }
        }
        return $replys;
    }

    /**
     * 发送无索引回复
     * @param  [type] &$wechat [description]
     * @param  [type] $main    [description]
     * @return [type]          [description]
     */
    private function subscribeSource3(&$wechat, $main)
    {
        $r      = $main['cfg'];
        $replys = [];
        if ($r['enable'] && $r['material']) {
            $r['material'] = json_decode($r['material'], true);
            $ids           = getByField($r['material'], 'id', 'str');
            $materials     = D('Material')->where("id in (" . $ids . ") and state=1 ")->getField('id,type,title,info');
            foreach ($r['material'] as $v) {
                $item                    = $materials[$v['id']];
                $replys[$item['type']][] = $item;
            }
        }
        return $replys;
    }

    /**
     * 发送菜单点击消息
     * @param  [type] &$wechat [description]
     * @param  [type] $main    [description]
     * @return [type]          [description]
     */
    private function subscribeSource4(&$wechat, $main)
    {
        $r           = $main['cfg'];
        $openid      = $wechat->getProperty('openid');
        $wechat_info = $wechat->getProperty('wechat_info');
        $wechat_id   = $wechat_info['id'];
        $hour        = $r['cycle']['hour'];
        $timers      = $r['cycle']['times'];
        if (!$hour || !$timers) {
            return false;
        }

        $end = time() - $hour * 3600;
        $has = D('wechat_clickmenu_log')->where(array('wechat_id' => $wechat_id, 'openid' => $openid, 'created' => array('gt', $end)))->count();

        $replys = [];
        if ($r['enable'] && $r['material'] && ($has < $timers)) {
            D('wechat_clickmenu_log')->add(array(
                'openid'    => $openid,
                'created'   => time(),
                'wechat_id' => $wechat_id,
            ));
            $r['material'] = json_decode($r['material'], true);
            $ids           = getByField($r['material'], 'id', 'str');
            $materials     = D('Material')->where("id in (" . $ids . ") and state=1 ")->getField('id,type,title,info');
            foreach ($r['material'] as $v) {
                $item                    = $materials[$v['id']];
                $replys[$item['type']][] = $item;
            }
        }
        return $replys;
    }

    /**
     * 突破防盗链抓取图片
     * @param  [type]
     * @param  string
     * @return [type]
     */
    private function downloadImageFromUrl($url, $path = "./Upload/wechat/qrcode/")
    {
        // 因为不知道最后接受到的文件是什么格式，先建立一个临时文件，用于保存
        $tmpFile = tempnam(sys_get_temp_dir(), 'image');

        # 文件下载 BEGIN #

        // 打开临时文件，用于写入（w),b二进制文件
        $resource = fopen($tmpFile, 'wb');

        // 初始化curl
        $curl = curl_init($url);
        // 设置输出文件为刚打开的
        curl_setopt($curl, CURLOPT_FILE, $resource);
        // 不需要头文件
        curl_setopt($curl, CURLOPT_HEADER, 0);
        // 执行
        curl_exec($curl);
        // 关闭curl
        curl_close($curl);

        // 关闭文件
        fclose($resource);

        # 文件下载 END #

        // 获取文件类型
        if (function_exists('exif_imagetype')) {
            // 读取一个图像的第一个字节并检查其签名(这里需要打开mbstring及php_exif)
            $fileType = exif_imagetype($tmpFile);
        } else {
            // 获取文件大小，里面第二个参数是文件类型 （这里后缀可以直接通过getimagesize($url)来获取，但是很慢）
            $fileInfo = getimagesize($tmpFile);
            $fileType = $fileInfo[2];
        }

        // 根据文件类型获取后缀名
        $extension = image_type_to_extension($fileType);

        // 计算指定文件的 MD5 散列值，作为保存的文件名，重复下载同一个文件不会产生重复保存，相同的文件散列值相同
        $md5FileName = md5_file($tmpFile);

        // 最终保存的文件
        $returnFile = $path . $md5FileName . $extension;

        // 检查传过来的路径是否存在，不存在就创建
        if (!is_dir($path)) {
            mkdir($path, 0777, true);
        }

        // 复制临时文件到最终保存的文件中
        copy($tmpFile, $returnFile);

        // 释放临时文件
        @unlink($tmpFile);

        // 返回保存的文件路径
        return '/' . ltrim($returnFile, './');
    }

    private function fullServerPath($path)
    {
        if (strpos($path, 'http') === false) {
            $path = $_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'] . '/' . ltrim($path, '/');
        }
        return $path;
    }

    private function getMediaId($item, $type, &$wechat)
    {

        $wechat_info = $wechat->getProperty('wechat_info');
        $wechat_id   = $wechat_info['id'];
        $f           = D('WechatMedia')->where(array('material_id' => $item['id'], 'wechat_id' => $wechat_id))->find();

        if (!$f) {
            $info = json_decode($item['info'], true);

            $media_id = $this->uploadMedia($wechat, $info['file'], $type, $info);

            $info_media = array(
                'wechat_id'   => $wechat_id,
                'material_id' => $item['id'],
                'media_id'    => $media_id,
            );
            D('WechatMedia')->add($info_media);

        } elseif (!$f['media_id']) {
            $info = json_decode($item['info'], true);

            $media_id = $this->uploadMedia($wechat, $info['file'], $type, $info);

            $info_media = array(

                'media_id' => $media_id,
            );
            D('WechatMedia')->where(array('id' => $f['id']))->save($info_media);
        } else {
            $media_id = $f['media_id'];
        }
        return $media_id;

    }

    /**
     *  保存多媒体资源
     * @param  [type] $wechat_id [description]
     * @param  [type] $file      [description]
     * @param  [type] $type      [description]
     * @return [type]            [description]
     */
    private function uploadMedia(&$wechat, $file, $type = 'image',$info=null)
    {

        $fdata = array('media' => '@' . realpath(ltrim($file, '/')));

        $video_info = [];
        if ($type == 'video') {
            $video_info = array('title' => $info['title'], 'introduction' => $info['desc']);
        }

        $r = $wechat->uploadForeverMedia($fdata, $type, $video_info);

        return $r;

    }

    /**
     * 创建目录
     * @param  [type]
     * @return [type]
     */
    private function mkDirs($dir)
    {
        if (!is_dir($dir)) {
            if (!$this->mkDirs(dirname($dir))) {
                return false;
            }
            if (!mkdir($dir, 0777)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 下载用户头像
     * @param  [type]
     * @param  [type]
     * @param  string
     * @return [type]
     */
    private function downloadAvatar($f, $openid, $path = 'Upload/Wechat/avatar/')
    {

        if (!is_dir($path)) {
            $this->mkDirs($path);
        }
        $h = $this->curlGetFile($f);
        if ($h) {
            $newf = $path . $openid . '.jpg';
            @file_put_contents($newf, $h);
            unset($h);
        } else {
            $newf = $f;
        }
        return $newf;
    }

    /**
     *处理事件
     **/
    private function doEvent(&$wechat)
    {
        $message     = $wechat->getProperty('message');
        $openid      = $wechat->getProperty('openid');
        $wechat_info = $wechat->getProperty('wechat_info');
        $wechat_id   = $wechat_info['id'];
        $company_uid = $wechat_info['company_uid'];
        if (F('evnet_' . $openid . '_' . $message['CreateTime'])) {
            return false;
        } else {
            F('evnet_' . $openid . '_' . $message['CreateTime'], 1);
        }
        switch ($message['Event']) {
            case 'subscribe':
            
                $this->eventSubscribe($wechat);
                break;
            case 'SCAN':
                $this->qrcodeWithSubsribe($wechat,0);
                break;
            case 'unsubscribe':
                $rel = array(
                    'openid' => $openid,
                );
                $this->eventCancelSubcribe($rel);

                break;

            case 'CLICK':
                $eventKey = $message['EventKey'];
                $replys   = [];
                if (strpos($eventKey, 'material_') !== false) {
                    $material_id = str_replace('material_', '', $eventKey);
                    $replys_cfg  = $this->buildMenuMessage($material_id);

                }

                //附加点击事件
                $replys_click = $this->subscribeSource($wechat, 4);

                if ($replys_click) {
                    foreach ($replys_cfg as $k => $v) {
                        $replys[$k] = $v;
                        foreach ($replys_click as $k1 => $v1) {
                            if (isset($replys[$k1])) {
                                $replys[$k1] = array_merge($replys[$k], $v1);
                            } else {

                                $replys[$k1] = $v1;
                            }

                        }
                    }
                } else {
                    $replys = $replys_cfg;
                }

                $replys && $this->sendMultipleMessage($wechat, $replys, 1);

                break;

            case $wechat::EVENT_LOCATION:
                $this->doEventLocation($wechat);

        }
    }

    /**
     * @param  事件回复，获取素材
     * @return [type]
     */
    private function buildMenuMessage($id)
    {
        $rs                 = D('Material')->where(array('id' => $id))->find();
        $res[$rs['type']][] = $rs;
        return $res;
    }

    /**
     * 取消关注
     * @param  [type] $rel [description]
     * @return [type]      [description]
     */
    private function eventCancelSubcribe($rel)
    {
        # 取消关注
        $openid = $rel['openid'];
        D('WechatFans')->where(array('openid' => $openid))->save(
            array('follow_state' => 0, 'follow_time' => '')
        );

        $last_id = D('WechatQrfans')->where("openid='" . $openid . "'  ")->order('id desc')->getField('id');
        D('WechatQrfans')->where(array('id'=>$last_id))->save(array('canceled' => time(), 'is_unsubsribe' => 1));

    }

    /**
     * @param  地理位置上报处理，被动接收
     * @return [type]
     */
    private function doLocation(&$wechat)
    {
        $message     = $wechat->getProperty('message');
        $wechat_info = $wechat->getProperty('wechat_info');
        $openid      = $wechat->getProperty('openid');
        $position    = $message['Location_X'] . ',' . $message['Location_Y'];
        D('WechatFans')->where(array('openid' => $openid))->save(array('position' => $position));

    }

    /**
     * 地理位置处理，来自菜单推送
     * @param  [type]
     * @return [type]
     */
    private function doEventLocation(&$wechat)
    {
        $message     = $wechat->getProperty('message');
        $wechat_info = $wechat->getProperty('wechat_info');
        $openid      = $wechat->getProperty('openid');
        $position    = $message['Latitude'] . ',' . $message['Longitude'];
        D('WechatFans')->where(array('openid' => $openid))->save(array('position' => $position));

    }

    /**
     *  保存多媒体资源
     * @param  [type] $wechat_id [description]
     * @param  [type] $file      [description]
     * @param  [type] $type      [description]
     * @return [type]            [description]
     */
    private function saveMedia($access_token, $file, $type = 'image')
    {
        if (strpos($file, 'http') === false) {
            $url = "https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=" . $access_token . "&type=" . $type;

            $fdata                                    = array('media' => new \CURLFile(realpath(ltrim($file, '/'))));
            $title                                    = basename($file);
            $type == 'video' && $fdata['description'] = '{"title":"' . $title . '", "introduction":"' . $title . '"}';
            $r                                        = https_request($url, $fdata);

            if ($r) {

                return $r['media_id'];
            }
            return '';
        }
    }

    private function downAvatar($f, $openid)
    {
        $path = 'Upload/Wechat/avatar/';
        if (!is_dir($path)) {
            mkdir($path);
        }
        $h = $this->curlGetFile($f);
        if ($h) {
            $newf = $path . $openid . '.jpg';
            @file_put_contents($newf, $h);
            unset($h);
        } else {
            $newf = $f;
        }
        return $newf;
    }

    /**
     * 发送客服消息
     * @return [type] [description]
     */
    public function sendServiceMessage()
    {
        $now    = time();
        $needed = D('WechatServiceMessage')->where(array('sendtime' => array('ELT', $now), 'sended' => 0))->select();

        $needed = $this->disturb($needed);

        if ($needed) {
            foreach ($needed as $v) {
                $ids_material[] = $v['material_id'];
                $ids_wechats[]  = $v['wechat_id'];
            }

            $materials = D('Material')->where(array('id' => array('in', $ids_material)))->getField('id,type,info');
            $wechats   = D('Wechat')->where(array('id' => array('in', $ids_wechats)))->getField('id,wechat_appid');
            $ls;
            foreach ($needed as $v) {
                $ls[$v['wechat_id']][$v['openid']][] = $v;
            }

            foreach ($ls as $k => $v) {
                $wechat_info = $this->getWechatCache($v['wechat_id']);
                $options     = array(
                    'token'          => $wechat_info['wechat_token'], //填写你设定的key
                    'encodingaeskey' => $wechat_info['wechat_encodingaeskey'], //填写加密用的EncodingAESKey
                    'appid'          => $wechat_info['wechat_appid'], //填写高级调用功能的app id
                    'appsecret'      => $wechat_info['wechat_appsecret'], //填写高级调用功能的密钥
                );

                $ws = \TPWechat::getInstance($options);
                $ws->setProperty(array(
                    'openid'      => $k,
                    'wechat_info' => $wechat_info,
                ));
                $ws->setMessageEncrypType(''); //必须明文

                foreach ($v as $k1 => $v1) {
                    $replys = [];
                    foreach ($v1 as $v2) {
                        $temp                    = $materials[$v2['material_id']];
                        $replys[$temp['type']][] = $temp;
                    }

                    $xml = $this->doMessage($ws, $replys, true);
                    foreach ($xml as $rv) {
                        $rv           = xmlToArr($rv);
                        $rv['touser'] = $k1;
                        $info_send    = $this->replyToService($rv);
                        $ws->sendCustomMessage($info_send);
                    }

                    D('WechatServiceMessage')->where(array('openid' => $k1))->save(array('sended' => 1));

                }
                unset($ws);
            }

        }
        $this->success('关注时延时回复发送成功！', "javascript:window.opener=null;window.close();");
    }

    private function disturb($r)
    {
        $now = date('Y-m-d H:i');
        foreach ($r as $k => $v) {
            if (!$v['outoftime']) {
                continue;
            }

            $timer = explode(',', $v['outoftime']);
            $start = date('Y-m-d') . ' ' . $timer[0];
            $end   = $timer[1] > $timer[0] ? date('Y-m-d') . ' ' . $timer[0] : date('Y-m-d', strtotime('+1 day')) . ' ' . $timer[0];
            if ($now > $start && $now < $end) {
                unset($r[$k]);
            }

        }
        return $r;
    }

    private function curlGetFile($url)
    {
        $headers   = array();
        $headers[] = 'Origin: http://data.jingzhunshuju.com';
        $headers[] = 'Referer: http://data.jingzhunshuju.com';
        $headers[] = 'User-Agent: Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3702.0 Safari/537.36';

        $ch = curl_init();

        $opt = array();

        $opt[CURLOPT_URL] = $url;

        $opt[CURLOPT_HEADER] = false;

        $opt[CURLOPT_CONNECTTIMEOUT] = 15;

        $opt[CURLOPT_TIMEOUT] = 15;

        $opt[CURLOPT_AUTOREFERER] = true;

        $opt[CURLOPT_USERAGENT] = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.47 Safari/536.11';

        $opt[CURLOPT_RETURNTRANSFER] = true;

        $opt[CURLOPT_FOLLOWLOCATION] = true;

        $opt[CURLOPT_MAXREDIRS] = 10;
        //user defined opt

        if (!empty($opt)) {
            foreach ($opt as $k => $v) {
                $opt[$k] = $v;
            }
        }

        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // 从证书
        curl_setopt_array($ch, $opt);
        $r = curl_exec($ch);
        unset($ch);

        return $r;
    }

    /**
     * 生成场景二维码
     * @param  [type]
     * @param  [type]
     * @param  [type]
     * @param  string
     * @return [type]
     */
    public function createQr($wechat_id, $scene_id, $stype, $time = '')
    {

        $type        = $stype == 1 ? 0 : 2;
        $wechat_info = $this->getWechatCache($wechat_id);
        $options     = array(
            'token'          => $wechat_info['wechat_token'], //填写你设定的key
            'encodingaeskey' => $wechat_info['wechat_encodingaeskey'], //填写加密用的EncodingAESKey
            'appid'          => $wechat_info['wechat_appid'], //填写高级调用功能的app id
            'appsecret'      => $wechat_info['wechat_appsecret'], //填写高级调用功能的密钥
        );
        $wechat = \TPWechat::getInstance($options);
        $expire = strtotime($time) - time();
        if ($expire <= 0) {
            $expire = 1;
        }

        $r = $wechat->getQRCode($scene_id, $type, $expire);

        return $r;

    }

    /**
     * 获取公众号缓存信息
     * @param  [type] $wechat_id [description]
     * @param  string $field     [description]
     * @return [type]            [description]
     */
    private function getWechatCache($wechat_id, $field = '')
    {
        $key = 'wechat_cache_' . $wechat_id;
        if (!F($key)) {
            $cache = D('wechat')->where(array('id' => $wechat_id))->find();
            F($key, $cache);
        } else {
            $cache = F($key);
        }
        $res = $field ? $cache[$field] : $cache;
        return $res;
    }

    /**
     * 菜单同步处理
     * @param  [type]
     * @param  [type]
     * @return [type]
     */
    public function syncMenu($wechat_id, $menu)
    {

      

        $info_wechat = $this->getWechatCache($wechat_id);
        $options     = array(
            'token'          => $info_wechat['wechat_token'], //填写你设定的key
            'encodingaeskey' => $info_wechat['wechat_encodingaeskey'], //填写加密用的EncodingAESKey
            'appid'          => $info_wechat['wechat_appid'], //填写高级调用功能的app id
            'appsecret'      => $info_wechat['wechat_appsecret'], //填写高级调用功能的密钥
        );
        $wechat = \TPWechat::getInstance($options);
        $r      = $wechat->createMenu($menu);
        return $r;
    }

    /**
     * 抓取公众号粉丝
     * @return [type]
     */
    public function fetchFans($wechat_id)
    {

        $wechat_info = $this->getWechatCache($wechat_id);

        $options = array(
            'token'          => $wechat_info['wechat_token'], //填写你设定的key
            'encodingaeskey' => $wechat_info['wechat_encodingaeskey'], //填写加密用的EncodingAESKey
            'appid'          => $wechat_info['wechat_appid'], //填写高级调用功能的app id
            'appsecret'      => $wechat_info['wechat_appsecret'], //填写高级调用功能的密钥
        );
        $wechat = \TPWechat::getInstance($options);

        $flag        = 1;
        $ls          = [];
        $next_openid = '';
        //获取标签

        $tags = $wechat->getUserTags();
        $this->saveWechatTags($tags, $wechat_id);
        if ($tags) {
            foreach ($tags as $v) {
                $item = array(
                    'wechat_id' => $wechat_id,
                    'tag_id'    => $v['id'],
                    'tag_name'  => $v['name'],
                    'count'     => $v['count'],
                );
                D('WechatTags')->add($item);

            }
        }

        while ($flag == 1) {
            $users = $wechat->getUserList($next_openid);

            $next_openid = $users['next_openid'];
            if (!$users['data']) {
                $flag = 0;
                break;
            } else {
                $ls = array_merge($ls, $users['data']['openid']);
            }
        }
        foreach ($ls as $openid) {
            $userinfo = $wechat->getUserInfo($openid);
            $f        = D('WechatFans')->where(array('openid' => $openid))->field('openid')->find();
            $item     = array(
                'cid'          => $wechat_id,
                'nick_name'    => $userinfo['nickname'],
                'avatar'       => $this->downloadAvatar($userinfo['headimgurl'], $openid, 'upload/Wechat/' . $wechat_id . '/avatar/'),
                'sex'          => (int) $userinfo['sex'] - 1,
                'region'       => $userinfo['province'] . '-' . $userinfo['city'],
                'follow_time'  => date('Y-m-d H:i:s', $userinfo['subscribe_time']),
                'update_time'  => date('Y-m-d H:i:s'),
                'num_state'    => 1,
                'openid'       => $openid,
                'company_uid'  => $wechat_info['company_uid'],
                'follow_state' => 1,
                'groupid'      => $userinfo['groupid'],
                'tagid_list'   => json_encode($userinfo['tagid_list'], JSON_UNESCAPED_UNICODE),
            );
            if (!$f) {
                D('WechatFans')->add($item);

            } else {
                unset($item['num_state']);
                unset($item['openid']);
                D('WechatFans')->where(array('openid' => $openid))->save($item);

            }
            sleep(0);
        }
        return true;

    }

    /**
     * 主动发送客服消息，客服对话
     * @param  [type]
     * @param  [type]
     * @return [type]
     */
    public function sendDialogMessage($msg, $openid)
    {

        $info = D('wechat_fans')->where(array('openid' => $openid))->find();

        $wechat_id = $info['cid'];

        $wechat_info = $this->getWechatCache($wechat_id);
        $options     = array(
            'token'          => $wechat_info['wechat_token'], //填写你设定的key
            'encodingaeskey' => $wechat_info['wechat_encodingaeskey'], //填写加密用的EncodingAESKey
            'appid'          => $wechat_info['wechat_appid'], //填写高级调用功能的app id
            'appsecret'      => $wechat_info['wechat_appsecret'], //填写高级调用功能的密钥
        );
        $wechat = \TPWechat::getInstance($options);
        $wechat->setProperty(array(
            'openid'      => $openid,
            'wechat_info' => $wechat_info,
        ));

        if (is_string($msg)) {
            //默认文本类型
            $rv = array(
                'MsgType' => 'text',
                'touser'  => $openid,
                'Content' => $msg,
            );
        } else {
            $replys;
            $replys[$msg['type']][] = $msg;
            $xml                    = $this->doMessage($wechat, $replys, true);

            $rv           = xmlToArr($xml[0]);
            $rv['touser'] = $openid;

        }
        $info_send = $this->replyToService($rv);
        if (is_array($msg) && $msg['material_id']) {
            $material_title    = D('material')->where(array('id' => $msg['material_id']))->getField('title');
            $rv['Content']     = '素材回复【ID:' . $msg['material_id'] . '，标题：' . $material_title . '】';
            $rv['material_id'] = $msg['material_id'];
        }

        $log = array(
            'info_type' => $rv['MsgType'],
            'openid'    => $openid,
            'created'   => date('Y-m-d H:i:s'),
            'wechat_id' => $wechat_id,
            'isrec'     => 2,
            'message'   => json_encode($rv, JSON_UNESCAPED_UNICODE),
        );
        D('wechat_message')->add($log);
        $r = $wechat->sendCustomMessage($info_send);

        return $r;

    }

    /**
     * 关注
     * @param  [type] $rel     [description]
     * @param  [type] &$wechat [description]
     * @return [type]          [description]
     */
    private function eventSubscribe(&$wechat)
    {
        $message = $wechat->getProperty('message');
        if ($message['EventKey']) {
            $this->qrcodeWithSubsribe($wechat,1);
        } else {
            $this->onlySubscribe($wechat);
        }
        $this->subscribeUserAdd($wechat);

    }

    /**
     * 普通关注
     * @return [type] [description]
     */
    private function onlySubscribe(&$wechat)
    {
        $message     = $wechat->getProperty('message');
        $wechat_info = $wechat->getProperty('wechat_info');
        $wechat_id   = $wechat_info['id'];
        $company_uid = $wechat_info['company_uid'];
        $openid      = $wechat->getProperty('openid');
        $replys      = $this->subscribeSource($wechat, 2);
        $this->sendMultipleMessage($wechat, $replys);

    }

    /**
     * 发送多条信息处理
     * @param  [type] &$wechat [description]
     * @param  [type] $replys  [description]
     * @return [type]          [description]
     * @return [type] $forceService         [强制用客服接口发送]
     */
    private function sendMultipleMessage(&$wechat, $replys, $forceServiceApi = 0)
    {

        $openid = $wechat->getProperty('openid');
        if ($this->countTwoArray($replys) > 1 || $forceService) {

            $wechat->setMessageEncrypType(''); //必须明文
            $ls_all = [];
            array_walk($replys, function ($v) use (&$ls_all) {
                foreach ($v as $v1) {
                    $ls_all[] = $v1;
                }
            });

            $info_new;

            foreach ($ls_all as $v) {
                $info_new[$v['type']][0] = $v;
                $xml                     = $this->doMessage($wechat, $info_new, true);
                unset($info_new[$v['type']]);
                foreach ($xml as $rv) {
                    $rv           = xmlToArr($rv);
                    $rv['touser'] = $openid;
                    $info_send    = $this->replyToService($rv);
                    $wechat->sendCustomMessage($info_send);
                }
                sleep(1);
            }
        } else {
            $this->doMessage($wechat, $replys);

        }
    }

    /**
     * 统计二维数组元素
     * @return [type] [description]
     */
    private function countTwoArray($ar)
    {
        if (!$ar) {
            return 0;
        }

        $i = 0;
        foreach ($ar as $v) {
            foreach ((array) $v as $v1) {
                $i++;
            }
        }
        return $i;
    }

    /**
     * 二维码扫关注
     * @return [type] [description]
     */
    private function qrcodeWithSubsribe(&$wechat,$qrtype=0)
    {

        $message     = $wechat->getProperty('message');
        $wechat_info = $wechat->getProperty('wechat_info');
        $wechat_id   = $wechat_info['id'];
        $company_uid = $wechat_info['company_uid'];
        $openid      = $wechat->getProperty('openid');
        //扫码事件
        $qrcode   = $message['EventKey'];
        $scene_id = str_replace('qrscene_', '', $message['EventKey']);
        //处理用户标签
        $this->doUserTags($scene_id, $wechat);

        $f      = D('WechatQrfans')->where(array('openid' => $openid, 'qrtype' => 1))->find();
        $is_new = $f ? 0 : 1;
        D('WechatQrfans')->add(array(
            'wechat_id' => $wechat_id,
            'openid'    => $openid,
            'qrscene'   => $scene_id,
            'created'   => $message['CreateTime'],
            'qrtype'    => $qrtype,
            'is_new'    => $is_new,
        ));

        //回复消息
        $replys = $this->qrcodeSource($wechat, $scene_id);

        $replys && $this->sendMultipleMessage($wechat, $replys);

    }

    /**
     * 二维码数据源
     * @param  [type] &$wechat  [description]
     * @param  [type] $scene_id [description]
     * @return [type]           [description]
     */
    private function qrcodeSource(&$wechat, $scene_id)
    {
        $r = D('wechat_qrcode')->where(array('id' => $scene_id))->getField('reply');
        if ($r) {
            $r = json_decode($r, true);
            if ($r['enable'] && $r['material']) {
                $r['material'] = json_decode($r['material'], true);
                $ids           = getByField($r['material'], 'id', 'str');
                $materials     = D('Material')->where("id in (" . $ids . ") and state=1 ")->getField('id,type,title,info');
                foreach ($r['material'] as $v) {
                    $item = $materials[$v['id']];
                    if (array_sum($v['donation_target']) > 0) {
                        $item['donation_target'] = $v['donation_target'];
                        $item['outoftime']       = $r['cycle_start'] . ',' . $r['cycle_end'];
                        $this->dispatcherService($wechat, $item);
                    } else {
                        $replys[$item['type']][] = $item;
                    }
                }
            }
        }
        return $replys;
    }

    /**
     * 关注用户添加
     * @return [type] [description]
     */
    private function subscribeUserAdd(&$wechat)
    {
        $wechat_info          = $wechat->getProperty('wechat_info');
        $wechat_id            = $wechat_info['id'];
        $company_uid          = $wechat_info['company_uid'];
        $openid               = $wechat->getProperty('openid');
        $userinfo             = $wechat->getUserInfo($openid);
        $userinfo['nickname'] = $wechat->getDefaultVal($userinfo['nickname'], '网友');
        $userinfo['avatar']   = $wechat->getDefaultVal($this->downloadAvatar($userinfo['headimgurl'], $openid, 'upload/Wechat/' . $wechat_id . '/avatar/'), '');

        $ex   = D('WechatFans')->where(array('openid' => $openid))->field('id,openid')->find();
        $data = array(
            'cid'          => $wechat_id,
            'nick_name'    => $userinfo['nickname'],
            'avatar'       => $userinfo['avatar'],
            'sex'          => (int) $userinfo['sex'] - 1,
            'region'       => $userinfo['province'] . '-' . $userinfo['city'],
            'follow_time'  => date('Y-m-d H:i:s'),
            'update_time'  => date('Y-m-d H:i:s'),
            'num_state'    => 1,
            'openid'       => $openid,
            'company_uid'  => $company_uid,
            'follow_state' => 1,
            'groupid'      => $userinfo['groupid'],
            'tagid_list'   => json_encode($userinfo['tagid_list'], JSON_UNESCAPED_UNICODE),
        );
        if (!$ex) {
            D('WechatFans')->add($data);

        } else {
            unset($data['openid']);
            unset($data['num_state']);
            D('WechatFans')->where(array('id' => $ex['id']))->save($data);
        }
    }

    private function doUserTags($scene_id, &$wechat)
    {
        $scene  = D('wechat_qrcode')->where(array('id' => $scene_id))->find();
        $rule   = json_decode($scene['rule'], true);
        $openid = $wechat->getProperty('openid');
        $tags   = $rule['tag1'];
        if ($rule['type'] != 2) {
            switch ($rule['type']) {
                case 1:
                    $old_tags = (array) $wechat->getUserTag($openid);
                    $new_tags = array_merge($tags, $old_tags);
                    $new_tags = array_unique($new_tags);
                    break;
                case 3:
                    $old_tags = (array) $wechat->getUserTag($openid);
                    foreach ($old_tags as $tagid) {
                        $wechat->cancelUserTag(array($openid), $tagid);
                    }
                    $new_tags = $tags;
                    break;
            }

            $r = [];

            foreach ($new_tags as $v) {
                $r[] = $wechat->addUserLabel(array($openid), $v);
            }
            $this->changeFansTag($openid, $new_tags);
        }

    }

    private function changeFansTag($openid, $tags)
    {
        $tags = json_encode($tags);
        D('wechat_fans')->where(array('openid' => $openid))->save(array('tagid_list' => $tags));
    }

    public function updateFansTagsApi($wechat_id, $openid, $new_tags)
    {

        $wechat_info = D('Wechat')->where(array('id' => $wechat_id))->find();

        $options = array(
            'token'          => $wechat_info['wechat_token'], //填写你设定的key
            'encodingaeskey' => $wechat_info['wechat_encodingaeskey'], //填写加密用的EncodingAESKey
            'appid'          => $wechat_info['wechat_appid'], //填写高级调用功能的app id
            'appsecret'      => $wechat_info['wechat_appsecret'], //填写高级调用功能的密钥
        );

        $wechat = \TPWechat::getInstance($options);

        $old_tags = (array) $wechat->getUserTag($openid);
        foreach ($old_tags as $tagid) {
            $wechat->cancelUserTag(array($openid), $tagid);
        }

        foreach ($new_tags as $v) {
            $r[] = $wechat->addUserLabel(array($openid), $v);
        }
        return $r;

    }

    private function saveWechatTags($tags, $wechat_id)
    {
        if ($tags) {
            foreach ($tags as $v) {
                $item = array(
                    'wechat_id' => $wechat_id,
                    'tag_id'    => $v['id'],
                    'tag_name'  => $v['name'],
                    'count'     => $v['count'],
                );
                $f = D('WechatTags')->where(array('wechat_id' => $wechat_id, 'tag_id' => $v['id']))->find();
                if (!$f) {
                    D('WechatTags')->add($item);
                } else {
                    unset($item['tag_id']);
                    unset($item['wechat_id']);
                    D('WechatTags')->where(array('wechat_id' => $wechat_id, 'tag_id' => $v['id']))->save(array('count' => $item['count']));
                }

            }
        }
    }

    /**
     * 延迟发送客服消息
     * @param  [type]
     * @return [type]
     */
    private function dispatcherService(&$wechat, $item)
    {
        $wechat_info = $wechat->getProperty('wechat_info');
        $wechat_id   = $wechat_info['id'];
        $openid      = $wechat->getProperty('openid');
        $now         = time();

        $target = explode('-', $item['donation_target']);
        $target = $target[0] * 3600 + $target[1] * 60 + $target[2];
        $item   = array(
            'wechat_id'   => $wechat_id,
            'sendtime'    => $now + $target,
            'material_id' => $item['id'],
            'openid'      => $openid,
        );
        D('WechatServiceMessage')->add($item);

    }

    /**
     * 格式化客服消息
     * @param  [type]
     * @return [type]
     */
    private function replyToService($r)
    {

        switch ($r['MsgType']) {
            case 'text':
                $info_send = array(
                    'touser'  => $r['touser'],
                    'msgtype' => 'text',
                    'text'    => array('content' => $r['Content']),
                );
                break;

            case 'image':
                $info_send = array(
                    'touser'  => $r['touser'],
                    'msgtype' => 'image',
                    'image'   => array('media_id' => $r['Image']['MediaId']),
                );
                break;
            case 'news':

                $news               = [];
                $temp               = isset($r['Articles']['item'][0]) ? $r['Articles']['item'][0] : $r['Articles']['item'];
                $news['articles'][] =
                array("title" => $temp['Title'],
                    "description" => $temp['Description'],
                    "url"         => $temp['Url'],
                    "picurl"      => $temp['PicUrl'],
                );

                $info_send = array(
                    'touser'  => $r['touser'],
                    'msgtype' => $r['MsgType'],
                    'news'    => $news,
                );

                break;

            case 'voice':
                $info_send = array(
                    'touser'  => $r['touser'],
                    'msgtype' => 'voice',
                    'voice'   => array('media_id' => $r['Voice']['MediaId']),
                );
                break;

            case 'video':

                $info_send = array(
                    'touser'  => $r['touser'],
                    'msgtype' => 'video',
                    'video'   => array(
                        'media_id'    => $r['Video']['MediaId'],
                        "title"       => "视频消息",
                        "description" => "",
                    ),
                );

                break;

        }
        return $info_send;
    }

    /**
     * 提交标签
     * @param  [type] $wechat_id [description]
     * @param  [type] $tags      [description]
     * @return [type]            [description]
     */
    public function postTags($wechat_id, $tags)
    {
        $wechat_info = D('Wechat')->where(array('id' => $wechat_id))->find();
        $options     = array(
            'token'          => $wechat_info['wechat_token'], //填写你设定的key
            'encodingaeskey' => $wechat_info['wechat_encodingaeskey'], //填写加密用的EncodingAESKey
            'appid'          => $wechat_info['wechat_appid'], //填写高级调用功能的app id
            'appsecret'      => $wechat_info['wechat_appsecret'], //填写高级调用功能的密钥
        );

        $wechat = \TPWechat::getInstance($options);
        $r      = $wechat->createTags($tags);

        return $r;
    }

    /**
     * 获取用户标签
     * @param  [type] $wechat_id [description]
     * @return [type]            [description]
     */
    public function getUserTags($wechat_id)
    {
        $wechat_info = D('Wechat')->where(array('id' => $wechat_id))->find();

        $options = array(
            'token'          => $wechat_info['wechat_token'], //填写你设定的key
            'encodingaeskey' => $wechat_info['wechat_encodingaeskey'], //填写加密用的EncodingAESKey
            'appid'          => $wechat_info['wechat_appid'], //填写高级调用功能的app id
            'appsecret'      => $wechat_info['wechat_appsecret'], //填写高级调用功能的密钥
        );

        $wechat = \TPWechat::getInstance($options);

        $tags = $wechat->getUserTags();
        $this->saveWechatTags($tags, $wechat_id);

        return $tags;
    }

    /**
     * 远程上传素材到公众号
     * @return [type] [description]
     */
    public function uploadForeverArticles()
    {
        $post = I('post.');
        $wechat_id   = $post['wechat_id'];
        $wechat_info = $this->getWechatCache($wechat_id);
        $options     = array(
            'token'          => $wechat_info['wechat_token'], //填写你设定的key
            'encodingaeskey' => $wechat_info['wechat_encodingaeskey'], //填写加密用的EncodingAESKey
            'appid'          => $wechat_info['wechat_appid'], //填写高级调用功能的app id
            'appsecret'      => $wechat_info['wechat_appsecret'], //填写高级调用功能的密钥
        );

        $wechat              = \TPWechat::getInstance($options);
        $infos['articles'][] = array(
            'title'                 => $post['title'],
            'thumb_media_id'        => $this->uploadMedia($wechat, $post['thumb'], 'image'),
            'author'                => $post['author'],
            'digest'                => $post['digest'],
            'content'               => $post['content'],
            'content_source_url'    => $post['url'],
            'need_open_comment'     => (int)$post['need_open_comment'],
            'only_fans_can_comment' => (int)$post['only_fans_can_comment'],
        );
        $r = $wechat->uploadForeverArticles($infos);
        return $r;

    }


   

}
